#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>

#include "service.h"


/**
 *  PARSE A SERVICE FROM STRING
**/
service_t* parse_service (char s[]) {
  char* p;
  int i = 0;

  /* allocazione della memoria necessaria a contenere la struttura */
  service_t* serv = malloc(sizeof(service_t));
  if (serv == NULL) {
    errx(EXIT_FAILURE, "malloc() error");
  }


  for(p = strtok(s, " "); ((i < MAX_SRV_FIELDS - 1) && (p != NULL)); p = strtok(NULL, " ")) {
    switch (i) {
      long n;
      char* p1;
      case 1:
        /* port: da `1` a `65535' */
        n = strtol(p, &p1, 0);
        if ((p1 == p) || (*p1 != '\0')) {
          /* il parsing non e' andato a buon fine */
          free(serv);
          return NULL;
        }
        if ((n < 1) || (n > 65535)) {
          /* la porta è invalida */
          free(serv);
          return NULL;
        }
        serv->port = (unsigned short)n;
        break;
      case 2:
        /* transport: `UDP` o `TCP` */
        if (strcasecmp(p, "TCP")==0) {
          serv->transport = TCP;
        }
        else  if (strcasecmp(p, "UDP")==0) {
                serv->transport = UDP;
              }
              else {
                /* tipo di trasporto invalido */
                free(serv);
                return NULL;
              }
        break;
      case 3:
        /* concurrency: `wait` o `nowait` */
        if (strcasecmp(p, "WAIT")==0) {
              serv->concurr = WAIT;
        }
        else  if (strcasecmp(p, "NOWAIT")==0) {
                serv->concurr = NOWAIT;
              }
              else {
                /* opzione di concorrenza invalido */
                free(serv);
                return NULL;
              }
        break;
      case 0:
        /* service_name */
        strncpy(serv->service_name, p, MAX_NAME_LEN);
        serv->service_name[MAX_NAME_LEN] = '\0';
        break;
      case 4:
        /* server_path */
        strncpy(serv->server_path, p, MAX_PATH_LEN);
        serv->server_path[MAX_PATH_LEN] = '\0';

        /* tentativo di estrazione del nome dal path: si seleziona la     *
         * sottostringa a partire dal carattere successivo all'ultimo '/' */
        p1 = strrchr(p, '/');
        if (p1 != NULL) {
          p = p1 + 1;
        }
        /* l'assenza dell'istruzione break fa in modo che il nome venga *
         * copiato in args[0] (convenzione usata da exec() )            */
      default:
        /* args */
        serv->args[i - 4] = malloc(strlen(p) + 1);
        if (serv->args[i - 4] == NULL) {
          /* malloc() fallita */
          errx(EXIT_FAILURE, "malloc() error");
        }
        strcpy(serv->args[i - 4], p);
        break;
    }
    ++i;
  }
  if (i < 5) {
    free(serv);
    return NULL;
  }
  /* NULL-terminazione dell'array `args` (rende piu' facile la exec() ) */
  serv->args[i - 4] = NULL;
  /* unset del campo pid */
  serv->pid = NO_PID;
  return serv;
}


/**
 *  BUILD SERVICES LIST FROM FILE
**/
service_t* parse_services(FILE* f) {
  service_t* prev_srv = NULL;
  service_t* curr_srv = NULL;
  service_t* first = NULL;

  char l[MAX_LINE];
  char *p;

  for (p = fgets(l, MAX_LINE, f); p != NULL; p = fgets(l, MAX_LINE, f)) {
    /* sostituzione del '\n' finale con '\0' */
    p = strrchr(l,'\n');
    if (p != NULL) {
      *p = '\0';
    }
    /* parsing del servizio */
    curr_srv = parse_service(l);
    if (curr_srv != NULL) {
      /* valido: */
      if (prev_srv == NULL) {
        /* primo valido: e' la head della lista */
        first = curr_srv;
      }
      else {
        /* non il primo valido: aggiornamento di prev->next */
        prev_srv->next = curr_srv;
      }
      /* set di curr->prev */
      curr_srv->prev = prev_srv;
      /* aggiornamento di prev (per la prossima iterazione) */
      prev_srv = curr_srv;
    }
  }
  /* set del next dell'ultimo elemento a NULL */
  if (curr_srv != NULL) {
    curr_srv->next = NULL;
  }
  return first;
}


/**
 *  SERVICE STRING REPRESENTATION
**/
char* service_to_str(const service_t* service) {
  char *s;
  s = (char*)malloc(12 + MAX_NAME_LEN + 5 + 3 + 6);
  sprintf(s, "service %s %d %s %s",
                service->service_name,
                service->port,
                (service->transport == UDP)? "UDP" : "TCP",
                (service->concurr == WAIT)? "Wait": "NoWait" );
  return s;
}


/**
 *  REMOVE SERVICE FROM LIST
**/
void remove_service(service_t** head, service_t* service) {
  char* serv_str = service_to_str(service);//debug
  fprintf(stderr, "removing %s\n", service_to_str(service)); //debug
  free(serv_str);//debug

  /* se c'e' un precedente, aggiornamento del suo campo next */
  if (service->prev != NULL) {
    service->prev->next = service->next;
  }
  /* altrimenti, aggiornamento della head della lista */
  else {
    *head = service->next;
    if (*head == NULL) {
      errx(EXIT_FAILURE, "Last service has been removed");
    }
  }
  /* se c'e' un successore, aggiornamento del suo campo prev */
  if (service->next != NULL) {
    service->next->prev = service->prev;
  }

  free(service);
  return;
}


/**
 *  SERVICE BY PID LOOKUP
**/
service_t* get_service_by_pid(service_t* head, pid_t pid) {
  service_t* serv;
  for (serv = head; (serv != NULL) && (serv->pid != pid); serv = serv->next);
  return serv;
}


/**
 *  SERVICE BY SOCKET LOOKUP
**/
service_t* get_service_by_sock(service_t* head, int fd) {
  service_t* serv;
  for (serv = head; (serv != NULL) && (serv->sock != fd); serv = serv->next);
  return serv;
}

